package com.sjgs.gis.service;

import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.MapViewport;
import org.geotools.referencing.CRS;
import org.geotools.renderer.GTRenderer;
import org.geotools.renderer.lite.StreamingRenderer;
import org.geotools.styling.SLDParser;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.opengis.filter.FilterFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 渲染服务
 * @author jxw
 */
@Service
public class RenderService {

    private final StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory();
    private final FilterFactory filterFactory = CommonFactoryFinder.getFilterFactory();

    public byte[] renderVector2Image(FeatureSource featureSource,String sldbody, int imageWidth) throws Exception {
        byte[] bytes = null;
        MapContent map = new MapContent();
        map.setTitle("Style Preview");
        Style style = createFromSLD(sldbody);
        Layer layer = new FeatureLayer(featureSource, style);
        map.addLayer(layer);

        MapViewport viewport = map.getViewport();
        ReferencedEnvelope maxBounds = null;
        CoordinateReferenceSystem mapCRS = viewport.getCoordinateReferenceSystem();
        //Zoom out to show all content
        ReferencedEnvelope dataBounds = layer.getBounds();
        CoordinateReferenceSystem dataCrs = dataBounds.getCoordinateReferenceSystem();
        if (!CRS.equalsIgnoreMetadata(dataCrs, mapCRS)) {
            dataBounds = dataBounds.transform(mapCRS, true);
        }
        if (maxBounds == null) {
            maxBounds = dataBounds;
        } else {
            maxBounds.expandToInclude(dataBounds);
        }
        viewport.setBounds(maxBounds);

        Rectangle imageBounds = null;
        ReferencedEnvelope mapBounds = null;
        try {
            mapBounds = map.getMaxBounds();
            double heightToWidth = mapBounds.getSpan(1) / mapBounds.getSpan(0);
            imageBounds = new Rectangle(
                    0, 0, imageWidth, (int) Math.round(imageWidth * heightToWidth));
        } catch (Exception e) {
            // failed to access map layers
            throw new RuntimeException(e);
        }

        BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_INT_RGB);
        //Graphics2D init
        Graphics2D gr = image.createGraphics();
        gr.setPaint(Color.WHITE);
        gr.fill(imageBounds);
        final Map<RenderingHints.Key, Object> hintsMap = new HashMap<RenderingHints.Key, Object>();
        //抗锯齿
        hintsMap.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        hintsMap.put(
                RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        // these two hints improve text layout in diagonal labels and reduce artifacts
        // in line rendering (without hampering performance)
        hintsMap.put(
                RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        hintsMap.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        gr.setRenderingHints(hintsMap);

        //render init
        RenderingHints hints = new RenderingHints(hintsMap);
        GTRenderer renderer = new StreamingRenderer();
        renderer.setMapContent(map);
        renderer.setJava2DHints(hints);

        Map<Object, Object> rendererParams = new HashMap<Object, Object>();
        try {
            // setup the renderer hints
            rendererParams.put("optimizedDataLoadingEnabled", Boolean.TRUE);
            rendererParams.put("renderingBuffer", 256 * 1024);
            rendererParams.put("maxFiltersToSendToDatastore", 10);
            rendererParams.put(
                    StreamingRenderer.SCALE_COMPUTATION_METHOD_KEY,
                    StreamingRenderer.SCALE_OGC);
            // used to be TEXT_RENDERING_ADAPTIVE always, but since java 7 calling drawGlyphVector
            // just generates very ugly results
            rendererParams.put(
                    StreamingRenderer.TEXT_RENDERING_KEY, StreamingRenderer.TEXT_RENDERING_OUTLINE);
            rendererParams.put(StreamingRenderer.LINE_WIDTH_OPTIMIZATION_KEY, true);
        } catch (Exception e) {
            ;
        }
        renderer.setRendererHints(rendererParams);

        //output image
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        renderer.paint(gr, imageBounds, mapBounds);
        ImageIO.write(image, "png", bos);
        bytes = bos.toByteArray();

        return bytes;
    }

    /**
     * Create a Style object from a definition in a SLD document
     */
    private Style createFromSLD(String sldbody) {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(sldbody.getBytes("UTF-8"));
            SLDParser sldParser = new SLDParser(styleFactory, bis);
            Style[] style = sldParser.readXML();
            return style[0];
        } catch (Exception e) {
            ;
        }
        return null;
    }
}
